package neutron

import (
	"errors"
	"os"
	"runtime"
	"strings"
	"time"

	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/socket"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/common/feature/phantom"
	"gddgame.cc/galaxy/project/neutron/neutron/controller"
	"gddgame.cc/galaxy/project/neutron/neutron/logic"
	"gddgame.cc/galaxy/project/neutron/neutron/metric"
	"gddgame.cc/galaxy/server/composer"
	"gddgame.cc/galaxy/server/ska"
	"gddgame.cc/galaxy/server/web"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/system"
)

const (
	Version = "1.0.0"
)

type Component struct {
	Config *logic.Config
	Cloud  *phantom.Config

	Web               web.Engine `inject:""`
	composer.Composer `inject:""`

	Binocle binocle.Agent `inject:""`
	Ska     ska.Engine    `inject:""`
}

func (c *Component) Init(register composer.Register, builder composer.Builder) error {
	var err error
	platform, err := phantom.PlatformWithConfig(c.Cloud)
	if err != nil {
		return err
	}
	if err := logic.Init(c.Config, platform, false); err != nil {
		return err
	}
	meta := map[string]string{}
	// 收集基础信息作为服务器元数据
	meta["cpu_num"] = utils.ToString(runtime.NumCPU())
	meta["host_name"], err = os.Hostname()
	if err != nil {
		return err
	}
	meta["ip"], err = system.LocalIPV4()
	if err != nil {
		return err
	}

	c.Ska.Register("topic", platform.Recorder())

	logger := builder.LoggerWrap("topic", "neutron").LoggerFields(map[string]interface{}{
		"project":   "neutron",
		"sub":       "neutron",
		"host_name": meta["host_name"],
		"ip":        meta["ip"],
	})

	// 注册为服务
	agent, err := c.Binocle.Service(binocle.Config{
		Name:    "neutron",
		Node:    c.Config.Node,
		Version: Version,
		Meta:    meta,
	})
	if err != nil {
		return err
	}
	if err := builder.BindInstance("neutron", agent); err != nil {
		return err
	}
	agent.OnObserved(func() {
		logger.Printf("agent observed")
	})
	go func() {
		ticker := time.NewTicker(time.Minute)
		for _ = range ticker.C {
			db := logic.Database()
			units, err := db.LoadAllUnit()
			if err != nil {
				continue
			}
			for _, unit := range units {
				if err := controller.RecoverUnit(builder, unit.Name); err != nil {
					logger.Errorln(err)
					continue
				}
			}
		}
	}()

	// 绑定计划任务
	agent.Schedule("command", func(args []string) error {
		if len(args) == 0 {
			return errors.New("args is empty")
		}
		logger.Printf("[ Schedule ] command: %s", strings.Join(args, " "))
		result, err := logic.ExecCommand(args[0], args[1:]...)
		logger.Printf("result: %s, %s", result, err)
		return err
	})
	event := utils.NewEvent()
	_ = event.On("unit/start", controller.StartUnit)

	for _, e := range event.Events() {
		agent.ListenDo(utils.ToString(e), func(request socket.Request) {
			var data []interface{}
			if err := request.Data(&data); err != nil {
				request.Response(err)
			} else if v, err := event.FireInterface(e, append([]interface{}{request}, data...)); err != nil {
				request.Response(err)
			} else {
				request.Response(v)
			}
		})
	}

	register.Attach(c.Web.Register(c.Config.Address, func(r web.Router, handler web.RouterHandler) error {
		swagger := BuildSwagger(builder, r, handler, &SwaggerConfig{})

		UnitHandle("/unit", builder, swagger("/unit"))

		// todo 外部上报接口定义
		return nil
	}))

	// 启动数据库中的监控进程
	db := logic.Database()
	units, err := db.LoadAllUnit()
	builder.Infoln(units, err)
	if err == nil {

		for _, unit := range units {
			_ = controller.RestartUnit(builder, unit.Name, false)
		}
	}

	// 绑定采集项
	if err := metric.Cpu(agent); err != nil {
		return err
	}
	//if err := metric.Df(agent); err != nil {
	//	return err
	//}
	//if err := metric.Disk(agent); err != nil {
	//	return err
	//}
	if err := metric.Memory(agent); err != nil {
		return err
	}
	//if err := metric.If(agent); err != nil {
	//	return err
	//}
	//if err := metric.Kernel(agent); err != nil {
	//	return err
	//}
	if err := metric.LoadAvg(agent); err != nil {
		return err
	}
	if err := metric.Netstat(agent); err != nil {
		return err
	}
	if err := metric.Sock(agent); err != nil {
		return err
	}
	return nil
}
